/* License Notice:
**
** This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
** This program is distributed in the hope that it will be useful,
**   but WITHOUT ANY WARRANTY; without even the implied warranty of
**   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
**   GNU General Public License for more details.
** You should have received a copy of the GNU General Public License
**   along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/**
 * @file event_manager.cpp
 * @author TooOld2Rock'nRoll
 * @see event_manager.hpp
 */
/*---- Includes ----*/
#include "event_manager.hpp"

#include "toolbox/time_keep.h"
#include "toolbox/debug.h"


/*---- Macros ----*/
/**
 * @brief Converts time in milliseconds to float point seconds.
 *
 * @param time_mili - the time to convert in milliseconds.
 *
 * @return The time converted to float point in seconds.
 */
#define MILLI_TO_SEC_CONVERT(time_mili)    time_mili/1000.f


/*---- Enumerations ----*/
extern "C" {
/**
 * @brief To be used internally for events not listed by SDL library.
 *
 * This creates an extra complexity layer to the event processing, but it also truly decouples the native SDL
 *   enumeration from our own since we need these extended event types in the library.
 */
typedef enum _AF_EventType_extended_
{
    AF_IN_GAME_EVENT = SDL_EventType::SDL_USEREVENT + 1, ///<To queue events generated inside the game.

    /* Other ArcadeFighter custom events as they are implemented */

    //always last!
    AF_USER_EVENT,  ///<To queue events generated by the user (very high level!) and not be mixed with SDL_USEREVENT
    _sdl_eventtype_extended_max ///<Just keep track of enum size.
} AF_EventType_extended_e;
}//end extern "C"


//I don't like this, but C++ enums are type strong...........
/** @brief Bitwise operator 'or' overload for a bitmask enum. */
inline constexpr EventManager::event_type_e operator| (const EventManager::event_type_e &lhs,
                                                       const EventManager::event_type_e &rhs)
{
    return (EventManager::event_type_e)((unsigned)lhs | (unsigned)rhs);
}//end operator|

/** @brief Bitwise operator 'and' overload for a bitmask enum. */
inline constexpr EventManager::event_type_e operator& (const EventManager::event_type_e &lhs,
                                                       const EventManager::event_type_e &rhs)
{
    return (EventManager::event_type_e)((unsigned)lhs & (unsigned)rhs);
}//end operator&

/** @brief Bitwise assignment operator 'or' overload for a bitmask enum. */
inline EventManager::event_type_e& operator|= (EventManager::event_type_e &lhs,
                                               const EventManager::event_type_e &rhs)
{
    lhs = (lhs | rhs);
    return lhs;
}//end operator|=

/** @brief Bitwise assignment operator 'and' overload for a bitmask enum. */
inline EventManager::event_type_e& operator&= (EventManager::event_type_e &lhs,
                                               const EventManager::event_type_e &rhs)
{
    lhs = (lhs & rhs);
    return lhs;
}//end operator&=


/*---- Methods Implementation ----*/
void EventManager::_clearListenerStruct (listener_s *ls)
{
    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    ls->ff_events_queue.clean ();

    for (EventManager::event_s *ev : ls->v_listen_for)
    {
        EventManager::clearEventStructure (*ev);
        delete (ev);
    }//end for (EventManager::event_s *ev : ls->v_listen_for)
    ls->v_listen_for.clear ();
}//end _freeListenerSt_cb ()

LinkedList<EventManager::listener_s>::iterator EventManager::_searchListenerByID (listenerID_t id)
{
    LinkedList<EventManager::listener_s>::iterator it = this->ll_listeners.begin ();

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    this->_message_queue_semaphore.lock ();
    for ( ; it != this->ll_listeners.end(); ++it)
    {
        if (it.read()->ID == id)
            break;
    }//end for ( ; it != this->ll_listeners.end(); ++it)
    this->_message_queue_semaphore.unlock ();

    return it;
}//end _searchListener_cb ()


/**
 * @brief Base Constructor.
 * @remark Hiden to make singleton safe.
 */
EventManager::EventManager ()
{
    _debug (INFO, "::%s () <--The EventManager singleton has been created!", __FUNCTION__);

    this->last_generated_id = 0;

    SDL_SetEventFilter (this->_SDL_eventFilter_cb, this);
}//End Constructor


/**
 * @brief Access the singleton instance.
 *
 * @return Reference to the EventManager singleton.
 */
EventManager& EventManager::instance ()
{
    static EventManager _instance;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    return _instance;
}//End instance ()


/**
 * @brief Destructor.
 */
EventManager::~EventManager ()
{
    EventManager::listener_s *ls = nullptr;

    _debug (INFO, "::%s () <--The EventManager singleton will be destroyed!", __FUNCTION__);

    SDL_SetEventFilter (nullptr, nullptr);

    this->_message_queue_semaphore.lock ();
    this->p_last_listener = nullptr;
    while ((ls = this->ll_listeners.remove ()))
    {
        EventManager::_clearListenerStruct (ls);
        delete ls;
    }
    this->_message_queue_semaphore.unlock ();
}//End Destructor


/**
 * @brief Constructor for the event struct.
 * Since we allocate memory inside the struct, this is the easiest way to handle its allocation.
 */
void EventManager::fillEventStructure (EventManager::event_s &ev, EventManager::event_type_e ev_type, void *ev_info, void *user_package)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    ev.type = ev_type;
    ev.p_user_package = user_package;
    if (ev_info)
    {
        switch (ev_type)
        {
            case EventManager::event_type_e::WINDOW:
                ev.p_data = new window_event_info_s ();
                std::memcpy (ev.p_data, ev_info, sizeof(window_event_info_s));
                break;
            //end case EventManager::event_type_e::WINDOW:

            case EventManager::event_type_e::KEYBOARD:
                ev.p_data = new keyboard_event_info_s ();
                std::memcpy (ev.p_data, ev_info, sizeof(keyboard_event_info_s));
                break;
            //end case EventManager::event_type_e::KEYBOARD:

            case EventManager::event_type_e::GAME:
                ev.p_data = new game_event_info_s ();
                std::memcpy (ev.p_data, ev_info, sizeof(game_event_info_s));
                break;
            //end case EventManager::event_type_e::KEYBOARD:

            /* Add other event types they are implemented */

            case EventManager::event_type_e::USER_DEFINED:
                ev.p_data = ev_info; //ev_info is the user package itself.
                ev.p_user_package = user_package;
                break;
            //end case EventManager::event_type_e::KEYBOARD:

            default:
                _debug (WARNING, "This event %u has information provided, but we don't recognize its data type!", ev_type);
                break;
        }//end switch (ev_type)
    }//end if (ev_info)
}//end fillEventStructure ()


/**
 * @brief Destructor for the event struct.
 * Since we allocate memory inside the struct, this is the easiest way to handle its deallocation.
 */
void EventManager::clearEventStructure (EventManager::event_s &ev)
{
    _debug (TRACE, "::%s ()", __FUNCTION__);

    if (!ev.p_data)
        return;

    switch (ev.type)
    {
        case EventManager::event_type_e::WINDOW:
            delete ((window_event_info_s *)ev.p_data);
            break;
        //end case EventManager::event_type_e::WINDOW:

        case EventManager::event_type_e::KEYBOARD:
            delete ((keyboard_event_info_s *)ev.p_data);
            break;
        //end case EventManager::event_type_e::KEYBOARD:

        case EventManager::event_type_e::GAME:
            delete ((game_event_info_s *)ev.p_data);
            break;
        //end case EventManager::event_type_e::KEYBOARD:

        /* Add other event types they are implemented */

        case EventManager::event_type_e::USER_DEFINED:
            //user is responsible for their pointers...
            break;
        //end case EventManager::event_type_e::KEYBOARD:

        default:
            _debug (WARNING, "This event %u has information provided, but we don't recognize its data type!", ev.type);
            break;
    }//end switch (this->type)
}//end cleanEvent ()


/**
 * @brief Informs the manager that you exists and want to keep updated to system wide events.
 *
 * If a callback is provided, events to that listener will be delivered directly to the listener, events won't be placed
 *    at the queue and using fetchEvent() is not necessary.<br>
 * The process will be blocked as the callback is doing its thing, so keep its workload light and return quickly.
 *
 * @param cb - optional callback to receive events as they are processes.
 * @param user_package - optional package to be forwarded to the listener when the callback is triggered.
 *
 * @return A unique identifier to access the class services.
 *
 * @remarks It's a little confusing, but this user_package is not the same as the one forwarded in the events themselves.<br>
 *  Considering the callback needs to be a static method, this is the place to forward your "this pointer" to the callback.
 *
 * @throws std::overflow_error - in case there id counter overflows and we can´t add more listeners (it should never happen though).
 */
listenerID_t EventManager::addListener (EventManager::EventListenerCallback cb, void *user_package)
{
    EventManager::listener_s *new_ls = nullptr;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    //check overflow
    if (this->last_generated_id + 1 < this->last_generated_id)
        throw std::overflow_error (fmt::format ("{0}:{1} - id generation has overflown!", __LINE__, __FILE__));

    ++this->last_generated_id;

    new_ls = new EventManager::listener_s ();
    new_ls->ID = this->last_generated_id;
    new_ls->func_process_events_callback = cb;
    new_ls->p_cb_package = user_package;
    this->ll_listeners.insert (new_ls);

    this->p_last_listener = new_ls;

    return this->last_generated_id;
}//End addListener ()


void EventManager::removeListener (LinkedList<EventManager::listener_s>::iterator *it)
{
    EventManager::listener_s *ls = nullptr;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    if (!it || !it->read ())
    {
        _debug (ERROR, "No listener provided to remove...");
        return;
    }

    this->_message_queue_semaphore.lock ();

    ls = this->ll_listeners.remove (*it);
    if (this->p_last_listener && this->p_last_listener->ID == ls->ID)
        this->p_last_listener = nullptr;

    this->_message_queue_semaphore.unlock ();

    EventManager::_clearListenerStruct (ls);
    delete ls;
}//End removeListener ()


/**
 * @brief Informs the manager you no longer need to receive system events.
 *
 * @param id - the identification received with addListener().
 */
void EventManager::removeListener (listenerID_t id)
{
    LinkedList<EventManager::listener_s>::iterator it;

    _debug (TRACE, "::%s (id=%u)", __FUNCTION__, id);

    it = this->_searchListenerByID (id);
    if (it != this->ll_listeners.end ())
        this->removeListener (&it);
    else
        _debug (ERROR, "This is a strange, but we can't find listener %u to remove!", id);
}//End removeListener ()


/**
 * @brief Private method to fill the event structure properly for any event type.
 *
 * @param id - the listener's identification.
 * @param ev_type - what type of event it is.
 * @param ev_info - specific data related to the event type state.
 * @param user_package - optional package to be forwarded to the listener when the event is triggered.
 *
 * @return Pointer to the actual event structure registered for the listener, null if listener can't be found.
 */
const EventManager::event_s * EventManager::listenForEvent (listenerID_t id, EventManager::event_type_e ev_type, void *ev_info, void *user_package)
{
    EventManager::event_s *ev = nullptr;
    EventManager::event_s *ret = nullptr;

    _debug (TRACE, "::%s (id=%u, type=%u)", __FUNCTION__, id, ev_type);

    //check if it's the same listener as last call.
    if (!this->p_last_listener || this->p_last_listener->ID != id)
        this->p_last_listener = this->_searchListenerByID(id).read ();

    if (this->p_last_listener)
    {
        ev = new EventManager::event_s ();
        EventManager::fillEventStructure (*ev, ev_type, ev_info, user_package);

        this->_message_queue_semaphore.lock ();

        this->p_last_listener->u_event_family_filter |= ev_type;
        this->p_last_listener->v_listen_for.push_back (ev);
        ret = this->p_last_listener->v_listen_for.back ();

        this->_message_queue_semaphore.unlock ();
    }//end if (this->p_last_listener)

    if (!ret)
        _debug (WARNING, "Trying to add event to listen for, but can't find the listener %u in the registered list!", id);

    return ret;
}//End listenForEvent ()


/**
 * @brief Register to listen for the Program Quit event.
 *
 * @param id - the listener's identification.
 * @param user_package - optional package to be forwarded to the listener when the event is triggered.
 *
 * @return Pointer to the actual event structure registered for the listener, null if listener can't be found.
 */
const EventManager::event_s * EventManager::listenForQuitEvent (listenerID_t id, void *user_package)
{
    _debug (TRACE, "::%s (id=%u, type='quit')", __FUNCTION__, id);

    return this->listenForEvent (id, event_type_e::QUIT, nullptr, user_package);
}//End listenForEvent ()


/**
 * @brief Register to listen for a window event.
 *
 * @param id - the listener's identification.
 * @param win_ev_id - the specific window state change to listen for.
 * @param user_package - optional package to be forwarded to the listener when the event is triggered.
 *
 * @return Pointer to the actual event structure registered for the listener, null if listener can't be found.
 */
const EventManager::event_s * EventManager::listenForEvent (listenerID_t id, SDL_WindowEventID win_ev_id, void *user_package)
{
    window_event_info_s ev_info = {win_ev_id, 0, 0};

    _debug (TRACE, "::%s (id=%u, type='window')", __FUNCTION__, id);

    return this->listenForEvent (id, event_type_e::WINDOW, &ev_info, user_package);
}//End listenForEvent ()


/**
 * @brief Register to listen for a keyboard event.
 *
 * @param id - the listener's identification.
 * @param key_st - for which key state change the event should trigger.
 * @param sc - the scancode of the desired key.
 * @param mod - modifiers for the keyboard state
 * @param user_package - optional package to be forwarded to the listener when the event is triggered.
 *
 * @return Pointer to the actual event structure registered for the listener, null if listener can't be found.
 */
const EventManager::event_s * EventManager::listenForEvent (listenerID_t id, key_state key_st, SDL_Scancode sc, SDL_Keymod mod, void *user_package)
{
    keyboard_event_info_s ev_info = {key_st, mod, sc};

    _debug (TRACE, "::%s (id=%u, type='keyboard')", __FUNCTION__, id);

    return this->listenForEvent (id, event_type_e::KEYBOARD, &ev_info, user_package);
}//End listenForEvent ()


/**
 * @brief Register to listen for a in game event.
 *
 * @param id - the listener's identification.
 * @param g_ev_id - the in game event id to trigger.
 * @param user_package - optional package to be forwarded to the listener when the event is triggered.
 *
 * @return Pointer to the actual event structure registered for the listener, null if listener can't be found.
 */
const EventManager::event_s * EventManager::listenForEvent (listenerID_t id, game_event_id_e g_ev_id, void *user_package)
{
    game_event_info_s ev_info = {g_ev_id, glm::vec3 (0.f)};

    _debug (TRACE, "::%s (id=%u, type='game')", __FUNCTION__, id);

    return this->listenForEvent (id, event_type_e::GAME, &ev_info, user_package);
}//End listenForEvent ()


/**
 * @brief Register to listen for a user defined event.
 *
 * @param id - the listener's identification.
 * @param user_package - optional package to be forwarded to the listener when the event is triggered.
 *
 * @return Pointer to the actual event structure registered for the listener, null if listener can't be found.
 */
const EventManager::event_s * EventManager::listenForUserEvents (listenerID_t id, void *user_package)
{
    _debug (TRACE, "::%s (id=%u, type='user')", __FUNCTION__, id);

    return this->listenForEvent (id, event_type_e::USER_DEFINED, nullptr, user_package);
}//End listenForEvent ()


/**
 * @brief Pushes a in game event into the queue.
 *
 * @param g_ev_id - the in game event identification.
 * @param pos - the position the event happened.
 */
void EventManager::push (game_event_id_e g_ev_id, const glm::vec3 &pos)
{
    SDL_Event sdl_ev;

    _debug (TRACE, "::%s (id=%u, type='game')", __FUNCTION__, g_ev_id);

    SDL_zero (sdl_ev);
    sdl_ev.type = AF_EventType_extended_e::AF_IN_GAME_EVENT;
    // sdl_ev.user.type = event_type_e::GAME; //for some reason, we can't set this value here!!! Event is not added to SDL queue.
    sdl_ev.user.code = g_ev_id;
    sdl_ev.user.data1 = new glm::vec3 (pos); //We must remember to delete this bad motherfucker when receiving it on the update()
    sdl_ev.user.data2 = nullptr;

    if (SDL_PushEvent (&sdl_ev) < 0)
        _debug (ERROR, "Can't push event to queue: %s", SDL_GetError ());
}//End queueEvent ()


/**
 * @brief Pushes an user defined event into the queue.
 *
 * @param ev_data - pointer to the event data (it won´t be copied, just forwarded).
 * @param user_data - pointer to a package to be delivered with the event (it won´t be copied, just forwarded).
 */
void EventManager::push (void *ev_data, void *user_data)
{
    SDL_Event sdl_ev;

    _debug (TRACE, "::%s (type='user')", __FUNCTION__);

    SDL_zero (sdl_ev);
    sdl_ev.type = AF_EventType_extended_e::AF_USER_EVENT;
    //sdl_ev.user.type = event_type_e::USER_DEFINED; //for some reason, we can't set this value here!!! Event is not added to SDL queue.
    sdl_ev.user.code = event_type_e::USER_DEFINED;
    sdl_ev.user.data1 = ev_data;
    sdl_ev.user.data2 = user_data;

    if (SDL_PushEvent (&sdl_ev) < 0)
        _debug (ERROR, "Can't push event to queue: %s", SDL_GetError ());
}//End queueEvent ()


/**
 * @brief Pops the oldest event in the listener queue of events.
 *
 * @param id - the listener's identification.
 *
 * @return Null if the queue is empty or a pointer to the first event.
 */
const EventManager::event_s* EventManager::pop (listenerID_t id)
{
    EventManager::event_s *ret = nullptr;

    _debug (TRACE, "::%s (id=%u)", __FUNCTION__, id);

    //check if it's the same listener as last call.
    if (!this->p_last_listener || this->p_last_listener->ID != id)
        this->p_last_listener = this->_searchListenerByID(id).read ();

    if (this->p_last_listener)
    {
        this->_message_queue_semaphore.lock ();
        this->p_last_listener->ff_events_queue.pop (ret);
        this->_message_queue_semaphore.unlock ();
    }//end if (this->p_last_listener)

    return ret;
}//End pop ()


/**
 * @brief A function pointer used for callbacks that watch the event queue.
 * @warning It may be running in a thread, it will continuously receive events independent of the game loop.
 *
 * @param event_manager_i - instance of the event manager that registered the filter callback.
 * @param event - the event that triggered the callback.
 *
 * @return Returns 1 to permit event to be added to the queue, and 0 to disallow it.
 * @see https://wiki.libsdl.org/SDL2/SDL_EventFilter
 */
int EventManager::_SDL_eventFilter_cb (void *event_manager_i, SDL_Event *sdl_event)
{
    bool pass_to_queue = true;
    int i = 0;
    EventManager::listener_s *listener = nullptr;
    EventManager::event_type_e looking_for = event_type_e::NONE;

    EventManager *obj_this = (EventManager *)event_manager_i;

    _debug (VERBOSE, "::%s ()", __FUNCTION__);

    //keep it in order! https://wiki.libsdl.org/SDL2/SDL_EventType
    switch (sdl_event->type)
    {
        case SDL_EventType::SDL_QUIT:
            looking_for = event_type_e::QUIT;
            break;
        //end case SDL_EventType::SDL_QUIT:

        case SDL_EventType::SDL_WINDOWEVENT:
            looking_for = event_type_e::WINDOW;
            break;
        //end case SDL_EventType::SDL_WINDOWEVENT:

        case SDL_EventType::SDL_KEYDOWN:
        case SDL_EventType::SDL_KEYUP:
            //HOLD event is handled in the update method, it's not SDL native and low priority.
            looking_for = event_type_e::KEYBOARD;
            break;
        //end case SDL_EventType::SDL_KEYDOWN:

        case AF_EventType_extended_e::AF_IN_GAME_EVENT:
            looking_for = event_type_e::GAME;
            break;
        //end case AF_EventType_extended_e::AF_IN_GAME_EVENT:

        /* other events as they are implemented */

        //always last...
        case AF_EventType_extended_e::AF_USER_EVENT:
            looking_for = event_type_e::USER_DEFINED;
            break;
        //end case AF_EventType_extended_e::AF_USER_EVENT:

        default:
            return true; //the event family does not interest us...
    }//end switch (event->type)

    obj_this->_message_queue_semaphore.lock ();

    //look for every listener interested in this event
    for (auto it : obj_this->ll_listeners)
    {
        listener = it.read ();

        if ((listener->u_event_family_filter & looking_for) != looking_for)
            continue; //listener is not expecting events from this family

        for (i = listener->v_listen_for.size (); i--; )
        {
            if (looking_for != listener->v_listen_for[i]->type)
                continue;

            switch (listener->v_listen_for[i]->type)
            {
                case event_type_e::QUIT:
                    listener->v_listen_for[i]->d_time_stamp = MILLI_TO_SEC_CONVERT (sdl_event->quit.timestamp);
                    break;
                //end case event_type_e::QUIT:

                case event_type_e::WINDOW:
                {
                    window_event_info_s *win_ev = (window_event_info_s *)listener->v_listen_for[i]->p_data;

                    if (win_ev->window_ev_id != sdl_event->window.event)
                        continue;

                    listener->v_listen_for[i]->d_time_stamp = MILLI_TO_SEC_CONVERT (sdl_event->window.timestamp);
                    win_ev->i_data1 = sdl_event->window.data1;
                    win_ev->i_data2 = sdl_event->window.data2;
                    break;
                }//end case event_type_e::WINDOW:

                case event_type_e::KEYBOARD:
                {
                    keyboard_event_info_s *key_ev = (keyboard_event_info_s *)listener->v_listen_for[i]->p_data;

                    //Check for desired modifiers
                    if ((key_ev->_modifyer & sdl_event->key.keysym.mod) != key_ev->_modifyer)
                        continue;
                    else if (key_ev->_scancode != sdl_event->key.keysym.scancode)
                        continue;
                    else if (key_ev->state != sdl_event->key.state)
                        continue;
                    else if (sdl_event->key.repeat)
                        continue; //we must discard key down events that are repeated, we have a HOLD event.

                    listener->v_listen_for[i]->d_time_stamp = MILLI_TO_SEC_CONVERT (sdl_event->key.timestamp);
                    break;
                }//end case event_type_e::KEYBOARD:

                /* other events as they are implemented */

                case event_type_e::GAME:
                {
                    game_event_info_s *game_ev = (game_event_info_s *)listener->v_listen_for[i]->p_data;

                    if (game_ev->game_ev_id != (unsigned)sdl_event->user.code)
                        continue;

                    listener->v_listen_for[i]->d_time_stamp = MILLI_TO_SEC_CONVERT (sdl_event->user.timestamp);
                    game_ev->v3_pos = *(glm::vec3 *)sdl_event->user.data1;

                    delete ((glm::vec3 *)sdl_event->user.data1);
                    break;
                }//end case event_type_e::GAME:

                case event_type_e::USER_DEFINED:
                    listener->v_listen_for[i]->d_time_stamp = MILLI_TO_SEC_CONVERT (sdl_event->user.timestamp);
                    listener->v_listen_for[i]->p_data = sdl_event->user.data1;
                    listener->v_listen_for[i]->p_user_package = sdl_event->user.data2;
                    break;
                //end case event_type_e::GAME:

                default:
                    continue;
            }//end switch (listener->v_listen_for[i]->type)

            pass_to_queue = false;
            if (!listener->func_process_events_callback)
                listener->ff_events_queue.push (listener->v_listen_for[i]);
            else
                listener->func_process_events_callback (listener->p_cb_package, *listener->v_listen_for[i]);
        }//end for (auto ev : listener->v_listen_for)
    }//end for (auto it : obj_this->ll_listeners)

    obj_this->_message_queue_semaphore.unlock ();

    return pass_to_queue;
}//end _SDL_eventFilter_cb ()


/**
 * @brief The main method of the manager, where all the system events are collected and sorted for the listeners.
 *
 * This method must be called once at the beginning of every logic game loop to sort the system events and queue for the listeners.<br>
 *
 * @remarks Although we are filtering and queuing events in a callback (so they can be handled in real time if needed),
 * some hardware interfaces still need a call to PumpEvents so their internal states can be updated.<br>
 *          We are also using this method to handle some low priority events and events that are not SDL native!
 */
void EventManager::update ()
{
    SDL_Event sdl_event;
    SDL_Keymod mod_state = KMOD_NONE;
    EventManager::listener_s *listener = nullptr;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    //SDL requires explicit call to populate the event queue!
    SDL_PumpEvents (); //ALL OTHER INTERFACES may require this to update its own hardware state, but we can call it only once per loop!!!

    this->interface_keyboard.update ();
    mod_state = this->interface_keyboard.getKeyModifiers ();

    /* add other interfaces as they are created */

    this->_message_queue_semaphore.lock ();

    //implementing it this way instead of pollEvents is better since we can only check for wanted events instead of
    //   receiving events and searching ALL the listeners for someone interested!
    //This may change in the future as more interfaces and custom events are added!
    for (auto it : this->ll_listeners)
    {
        listener = it.read ();

        //for now, we are looking just for key HOLD events.

        if ((listener->u_event_family_filter & event_type_e::KEYBOARD) != event_type_e::KEYBOARD)
            continue; //listener is not expecting events from this family

        for (EventManager::event_s *ev : listener->v_listen_for)
        {
            switch (ev->type)
            {
                case event_type_e::KEYBOARD:
                {
                    keyboard_event_info_s *key_ev = (keyboard_event_info_s *)ev->p_data;

                    //Check for desired modifiers
                    if ((key_ev->_modifyer & mod_state) != key_ev->_modifyer)
                        continue;
                    else if (key_ev->state != key_state::HOLD)
                        continue;
                    else if (key_ev->state != this->interface_keyboard.getKeyState(key_ev->_scancode))
                        continue;

                    ev->d_time_stamp = getHiResTime ();
                    break;
                }//end case event_type_e::KEYBOARD:


                /* Other Event Origins, like mouse, etc... */

                default:
                    continue;
            }//end switch (ev->type)

            if (!listener->func_process_events_callback)
                listener->ff_events_queue.push (ev);
            else
                listener->func_process_events_callback (listener->p_cb_package, *ev);
        }//end for (auto ev : listener->v_listen_for)
    }//end while ((listener = (EventManager::listener_s *)this->ll_listeners.next ()))


    this->_message_queue_semaphore.unlock ();

    //be sure none of our in game events remain in the SDL queue as we need to free some pointes....
    while (SDL_PeepEvents (&sdl_event, 1, SDL_GETEVENT,
                           AF_EventType_extended_e::AF_IN_GAME_EVENT, AF_EventType_extended_e::AF_IN_GAME_EVENT))
    {
        delete ((glm::vec3 *)sdl_event.user.data1);
    }//end while (SDL_PeepEvents (&sdl_event, 1, SDL_GETEVENT, ...

    //clear the event queue, any remaining events are of no interest.
    SDL_FlushEvents (SDL_FIRSTEVENT, SDL_LASTEVENT);
}//End processEvents ()


/**
 * @brief Waits for a key up event from any source that can trigger such events.
 * @remark The method will wait for 10sec for a event and exit if none received.
 *
 * This is a useful method to customize the user input mapping or "press any key to continue" events in isolation (not in the game loop).
 *
 * @param cb - optional callback to inform when a pertinent event is triggered.
 * @param user_package - data provided to be forwarded to the callback.
 *
 * @return <b>true</b> if the captured event is a key up event, <b>false</b> if timeout.
 */
bool EventManager::waitKeyUpEvent (EventManager::EventListenerCallback cb, void *user_package)
{
    SDL_Event sdl_event;
    EventManager::event_s ev;

    _debug (TRACE, "::%s ()", __FUNCTION__);

    while (1)
    {
        if (!SDL_WaitEventTimeout (&sdl_event, 10000))
            return false;

        //a switch would be better, but we need to break the while loop
        if (sdl_event.type == SDL_EventType::SDL_KEYUP)
        {
            if (cb)
            {
                keyboard_event_info_s ev_info = {key_state::UP, (SDL_Keymod)sdl_event.key.keysym.mod, sdl_event.key.keysym.scancode};
                EventManager::fillEventStructure (ev, EventManager::event_type_e::KEYBOARD, &ev_info, user_package);
                ev.d_time_stamp = MILLI_TO_SEC_CONVERT (sdl_event.key.timestamp);
            }//end if (cb)

            break;
        }//end if (sdl_event.type == SDL_EventType::SDL_KEYUP)

        /* add other input clicky interfaces as they are implemented */

        else
        {
            _debug (INFO, "The event %u triggered is not relevant.", sdl_event.type);
            continue;
        }//end else
    }//end while (1)

    if (cb)
    {
        cb (user_package, ev);
        EventManager::clearEventStructure (ev);
    }//end if (cb)

    return true;
}//End waitKeyUpEvent ()


/**
 * @brief Compares two events.
 *
 * @param ev1 - reference to the event to compare.
 * @param ev2 - reference to the event to compare.
 *
 * @return <b>false</b> if the events are different or <b>true</b>if they are equal.
 */
bool EventManager::isEqual (const EventManager::event_s &ev1, const EventManager::event_s &ev2)
{
    bool is_equal = false;

    _debug (TRACE, "::%s (ev1.type=%u, ev2.type=%u)", __FUNCTION__, ev1.type, ev2.type);

    if (ev1.type == ev2.type)
    {
        switch (ev1.type)
        {
            case EventManager::event_type_e::QUIT:
                is_equal = true;
                break;
                //end case EventManager::event_type_e::QUIT

            case EventManager::event_type_e::WINDOW:
                if (((window_event_info_s *)ev1.p_data)->window_ev_id == ((window_event_info_s *)ev2.p_data)->window_ev_id)
                    is_equal = true;
                break;
            //end case EventManager::event_type_e::WINDOW:

            case EventManager::event_type_e::KEYBOARD:
                if (memcmp (&ev1.p_data, &ev2.p_data, sizeof (keyboard_event_info_s)) == 0)
                    is_equal = true;
                break;
            //end case EventManager::event_type_e::KEYBOARD

            case EventManager::event_type_e::GAME:
                if (((game_event_info_s *)ev1.p_data)->game_ev_id == ((game_event_info_s *)ev2.p_data)->game_ev_id)
                    is_equal = true;
                break;
            //end case EventManager::event_type_e::GAME

            /* add others as they are implemented */

            default:
                _debug (WARNING, "Comparing unsupported event types!");
                break;
        }//end switch (event_s.type)
    }//end if (ev1.type == ev2.type)

    return is_equal;
}//End isEqual ()

